/*
** Xin YUAN, 2024, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////
#ifndef __CTL_RANDOM_H__
#define __CTL_RANDOM_H__
////////////////////////////////////////////////////////////////////////////////

#ifndef __CTL_DEF_H__
	#error random.h requires def.h to be included first.
#endif

////////////////////////////////////////////////////////////////////////////////
namespace CTL {
////////////////////////////////////////////////////////////////////////////////

//PseudoRandomNumber<t_uMultiplier, t_uAddend, t_uMax>
/*! \brief A class for pseudorandom number.

A class for pseudorandom number.
\tparam t_uMultiplier A multiplier.
\tparam t_uAddend An addend.
\tparam t_uMax The maximum value. The number range is [0, t_uMax).
*/
template <uint32_t t_uMultiplier, uint32_t t_uAddend, uint32_t t_uMax>
class PseudoRandomNumber
{
public:
	static constexpr const uint32_t c_uMax = t_uMax;

public:
	/*! \brief Constructor.

	Constructor.
	\param uSeed [in] The initial seed value.
	*/
	explicit PseudoRandomNumber(uint32_t uSeed = 1) noexcept : m_uSeed(uSeed)
	{
	}
	PseudoRandomNumber(const PseudoRandomNumber& src) noexcept : m_uSeed(src.m_uSeed)
	{
	}
	~PseudoRandomNumber() noexcept
	{
	}

	PseudoRandomNumber& operator=(const PseudoRandomNumber& src) noexcept
	{
		m_uSeed = src.m_uSeed;
		return *this;
	}

	/*! \brief Get the limits of random number.

	Get the limits of random number.
	\param uLow [out] Receive the low number.
	\param uHigh [out] Receive the high number.
	*/
	static void GetLimits(uint32_t& uLow, uint32_t& uHigh) noexcept
	{
		uLow = 0;
		uHigh = t_uMax;
	}

	/*! \brief Initialize a seed.

	Initialize a seed.
	\param uSeed [in] The initial seed value.
	*/
	void Init(uint32_t uSeed = (unsigned int)::time(NULL)) noexcept
	{
		m_uSeed = uSeed;
	}

	/*! \brief Get the next value.

	Get the next value.
	\return The next value.
	*/
	uint32_t Next() noexcept
	{
		m_uSeed = (m_uSeed * t_uMultiplier + t_uAddend) % t_uMax;
		return m_uSeed;
	}

private:
	uint32_t m_uSeed;  //!< seed
};

//RandomNumber1
typedef PseudoRandomNumber<2045, 1, 1048576>  RandomNumber1;
//RandomNumber2
typedef PseudoRandomNumber<2053, 13849, 65536>  RandomNumber2;

//AdvRandomNumberT<TRandomNumber>
/*! \brief A class for advanced random number.

A class for advanced random number.
\param TRandomNumber The basic random generator (e.g., PseudoRandomNumber<>).
*/
template <class TRandomNumber>
class AdvRandomNumberT : private TRandomNumber
{
private:
	typedef TRandomNumber  baseClass;

	enum {
		RANDSEED_DEFAULT = 13579,
		RANDSEED_INIT    = 3030303UL,
		RANDTABLE_SIZE   = 101
	};

	uint32_t m_ranTable[RANDTABLE_SIZE];  //random table
	uint32_t m_lastRand;  //last random number

public:
	/*! \brief Constructor.

	Constructor.
	*/
	AdvRandomNumberT() noexcept
	{
		this->Init(RANDSEED_INIT);
	}
	/*! \brief Initialize a seed.

	Initialize a seed.
	\param uSeed [in] The initial value.
	*/
	void Init(uint32_t uSeed = (unsigned int)::time(NULL)) noexcept
	{
		baseClass::Init(uSeed);
		for ( int32_t i = 0; i < RANDTABLE_SIZE; i ++ )
			m_ranTable[i] = baseClass::Next();
		m_lastRand = baseClass::Next();
	}
	/*! \brief Get the next value.

	Get the next value.
	\return The next value.
	*/
	uint32_t Next() noexcept
	{
		uint32_t uIndex = (m_lastRand * RANDTABLE_SIZE) / baseClass::c_uMax;
		m_lastRand = m_ranTable[uIndex];
		m_ranTable[uIndex] = baseClass::Next();
		return m_lastRand;
	}
};

//AdvRandomNumber1
typedef AdvRandomNumberT<RandomNumber1>  AdvRandomNumber1;
//AdvRandomNumber2
typedef AdvRandomNumberT<RandomNumber2>  AdvRandomNumber2;

////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
